use crate::dto;
use crate::service::Error;
use crate::service::segment::SegmentService;
use crate::service::segment_list::SegmentLisService;
use std::sync::Arc;
use wanmesh_common::error::ResponseError;

#[async_trait::async_trait]
pub trait SegmentListManagerService: Sync + Send {
    async fn add(
        &self,
        params: &dto::segment_list::AddParams,
    ) -> Result<dto::core::segment_list::AddResponseData, Error>;

    async fn list_info(
        &self,
        filter: &dto::core::segment_list::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::segment_list::SegmentListInfo>, Error>;

    async fn get_info(
        &self,
        segment_list_id: &str,
    ) -> Result<dto::segment_list::SegmentListInfo, Error>;

    async fn try_get_info(
        &self,
        segment_list_id: &str,
    ) -> Result<Option<dto::segment_list::SegmentListInfo>, Error>;

    async fn update(
        &self,
        segment_list_id: &str,
        params: &dto::core::segment_list::UpdateParams,
    ) -> Result<(), Error>;

    async fn delete(&self, segment_list_id: &str) -> Result<(), Error>;

    async fn count(&self, filter: &dto::core::segment_list::Filter) -> Result<u64, Error>;
}

pub struct DefaultSegmentListManagerService {
    segment_list_service: Arc<dyn SegmentLisService>,
    segment_service: Arc<dyn SegmentService>,
}

impl DefaultSegmentListManagerService {
    pub fn new(
        segment_list_service: Arc<dyn SegmentLisService>,
        segment_service: Arc<dyn SegmentService>,
    ) -> Self {
        DefaultSegmentListManagerService {
            segment_list_service,
            segment_service,
        }
    }

    async fn get_by_core(
        &self,
        segment_list: dto::core::segment_list::SegmentListItem,
    ) -> Result<dto::segment_list::SegmentListInfo, Error> {
        let endpoint_id_list = self
            .segment_service
            .list(segment_list.segment_list_id.as_str())
            .await?;

        Ok(dto::segment_list::SegmentListInfo {
            inner: segment_list,
            endpoint_id_list,
        })
    }
}

#[async_trait::async_trait]
impl SegmentListManagerService for DefaultSegmentListManagerService {
    async fn add(
        &self,
        params: &dto::segment_list::AddParams,
    ) -> Result<dto::core::segment_list::AddResponseData, Error> {
        let data = self.segment_list_service.add(&params.inner).await?;
        self.segment_service
            .add_list(
                data.segment_list_id.as_str(),
                params.endpoint_id_list.as_slice(),
            )
            .await?;
        Ok(data)
    }
    async fn list_info(
        &self,
        filter: &dto::core::segment_list::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::segment_list::SegmentListInfo>, Error> {
        let core_list = self
            .segment_list_service
            .list(filter, offset, limit)
            .await?;

        let mut list = Vec::new();

        for segment_list in core_list {
            let item = self.get_by_core(segment_list).await?;

            list.push(item);
        }

        Ok(list)
    }

    async fn get_info(
        &self,
        segment_list_id: &str,
    ) -> Result<dto::segment_list::SegmentListInfo, Error> {
        let segment_list = self
            .try_get_info(segment_list_id)
            .await?
            .ok_or(ResponseError::segment_list_not_found(segment_list_id))?;

        Ok(segment_list)
    }

    async fn try_get_info(
        &self,
        segment_list_id: &str,
    ) -> Result<Option<dto::segment_list::SegmentListInfo>, Error> {
        if let Some(segment_list) = self.segment_list_service.try_get(segment_list_id).await? {
            return Ok(Some(self.get_by_core(segment_list).await?));
        }
        Ok(None)
    }

    async fn update(
        &self,
        segment_list_id: &str,
        params: &dto::core::segment_list::UpdateParams,
    ) -> Result<(), Error> {
        self.segment_list_service
            .update(segment_list_id, params)
            .await
    }

    async fn delete(&self, segment_list_id: &str) -> Result<(), Error> {
        self.segment_list_service.delete(segment_list_id).await?;
        self.segment_service.delete_list(segment_list_id).await?;
        Ok(())
    }

    async fn count(&self, filter: &dto::core::segment_list::Filter) -> Result<u64, Error> {
        self.segment_list_service.count(filter).await
    }
}
